#pragma once
#include <etcd/Client.hpp>
#include <etcd/KeepAlive.hpp>
#include <etcd/Response.hpp>
#include <etcd/Watcher.hpp>
#include <etcd/Value.hpp>
#include <functional>
#include "logger.hpp"

namespace my
{
    // 服务注册客户端类
    class Registry
    {
    public:
        using ptr = shared_ptr<Registry>;
        Registry(const std::string &host)
            : _client(std::make_shared<etcd::Client>(host))
            , _keepAlive(_client->leasekeepalive(3).get())
            , _leaseId(_keepAlive->Lease())
        {
        }

        ~Registry()
        {
            _keepAlive->Cancel();
        }

        //注册
        bool registry(const std::string& key,const std::string& val)
        {
            auto resp = _client->put(key, val, _leaseId).get();
            if (resp.is_ok() == false)
            {
                std::cout << "新增数据失败：" << resp.error_message() << std::endl;
                return false;
            }
            return true;
        }

    private:
        std::shared_ptr<etcd::Client> _client;       // 客户端对象
        std::shared_ptr<etcd::KeepAlive> _keepAlive; // 租约保活对象
        uint64_t _leaseId;
    };

    // 服务发现客户端类
    class Discovery
    {
    public:
        using ptr = shared_ptr<Discovery>;
        using NotifyCallback = std::function<void(std::string, std::string)>;

        Discovery(const std::string &host, const std::string &basedir,
                  const NotifyCallback &put_cb, const NotifyCallback &del_cb)
            : _client(std::make_shared<etcd::Client>(host)),
              _putCB(put_cb), _delCB(del_cb)
        {
            //先进行服务发现,先获取到当前已有的数据
            auto resp = _client->ls(basedir).get();
            if (resp.is_ok() == false)
            {
                LOG_ERROR("获取服务信息数据失败：{}", resp.error_message());
            }

            int sz = resp.keys().size();
            for (int i = 0; i < sz; ++i)
            {
                if (_putCB)
                {
                    _putCB(resp.key(i), resp.value(i).as_string());
                }
            }

            // 然后进行事件监控，监控数据发生的改变并调用回调进行处理
            // bind,以便回调访问_putCB、_delCB
            _watcher = std::make_shared<etcd::Watcher>(*_client.get(), basedir,
                                                       std::bind(&Discovery::callback, this, std::placeholders::_1), true);
        }

        ~Discovery() {
            _watcher->Cancel();
        }

    private:
        void callback(const etcd::Response &resp)
        {
            if (resp.is_ok() == false)
            {
                LOG_ERROR("收到一个错误的事件通知: {}", resp.error_message());
                return;
            }
            for (auto const &ev : resp.events())
            {
                if (ev.event_type() == etcd::Event::EventType::PUT)
                {
                    if (_putCB)
                        _putCB(ev.kv().key(), ev.kv().as_string());
                    LOG_DEBUG("新增服务：{}-{}", ev.kv().key(), ev.kv().as_string());
                }
                else if (ev.event_type() == etcd::Event::EventType::DELETE_)
                {
                    if (_delCB)
                        _delCB(ev.prev_kv().key(), ev.prev_kv().as_string());
                    LOG_DEBUG("下线服务：{}-{}", ev.prev_kv().key(), ev.prev_kv().as_string());
                }
            }
        }

    private:
        NotifyCallback _putCB;
        NotifyCallback _delCB;
        std::shared_ptr<etcd::Client> _client;
        std::shared_ptr<etcd::Watcher> _watcher;
    };
}